package cn.aitrox.ry.service.oss.sdk;

import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.enumtype.RespCodeEnum;
import cn.aitrox.ry.service.oss.bean.UrlParams;
import cn.aitrox.ry.service.oss.bean.UrlParseResult;
import cn.aitrox.ry.service.oss.bean.UrlSignBody;
import cn.aitrox.ry.service.oss.bean.UrlValidResult;
import cn.aitrox.ry.service.oss.constant.ErrorConstant;
import cn.aitrox.ry.service.oss.dto.FileRestoreInfoDto;
import cn.aitrox.ry.util.FsUtil;
import cn.aitrox.ry.util.JSONUtil;
import cn.aitrox.ry.util.LogUtil;
import cn.aitrox.ry.util.MacUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

@Component
public class LocalOssSdk implements OssSdk {

    private static final Logger LOG = LoggerFactory.getLogger(LocalOssSdk.class);

    @Value("${local.prePath:}")
    private String prePath;

    @Value("${local.hostLan:}")
    private String hostLan;

    @Value("${local.hostWan:}")
    private String hostWan;

    @Value("${local.urlSecretKey:}")
    private String urlSecretKey;

    @Override
    public RespResult<FileRestoreInfoDto> getDownloadUrl(String resourceKey, Integer expireSeconds) {
        try {
            Long expireTime = null;
            if (null != expireSeconds) {
                expireTime = System.currentTimeMillis() + (expireSeconds * 1000);
            }

            UrlSignBody urlLanSignBody = new UrlSignBody(hostLan, expireTime, resourceKey);
            UrlSignBody urlWanSignBody = new UrlSignBody(hostWan, expireTime, resourceKey);

            String urlLanBody = JSONUtil.toJson(urlLanSignBody, urlLanSignBody.getClass());
            String urlWanBody = JSONUtil.toJson(urlWanSignBody, urlWanSignBody.getClass() );

            String lanSign = MacUtil.sign(urlSecretKey, urlLanBody, MacUtil.HMAC_SHA_256);
            String wanSign = MacUtil.sign(urlSecretKey, urlWanBody, MacUtil.HMAC_SHA_256);

            String subUrlLan = resourceKey + "?" + "sign=" + lanSign + "&version=" + UrlSignBody.VERSION_1 + "&expire=" + (null == expireTime ? "" : expireTime);
            String subUrlWan = resourceKey + "?" + "sign=" + wanSign + "&version=" + UrlSignBody.VERSION_1 + "&expire=" + (null == expireTime ? "" : expireTime);
            String urlLan = hostLan + subUrlLan;
            String urlWan = hostWan + subUrlWan;
            return RespResult.ok(new FileRestoreInfoDto(resourceKey, urlLan, urlWan));
        } catch (Exception e) {
            LOG.error(LogUtil.genException(e, "LocalOssSdk.getUrl happen exception", "key", resourceKey));
            return RespResult.error(e.getMessage());
        }
    }

    @Override
    public RespResult copyFile(String srcKey, String dstKey) {
        File srcFile = new File(prePath, srcKey);
        File destFile = new File(prePath, dstKey);
        if (!srcFile.exists() || !srcFile.isFile()) {
            return RespResult.error(RespCodeEnum.FILE_NOT_EXISTED);
        }
        boolean result = FsUtil.copyFile(srcFile, destFile);
        if (result) {
            return RespResult.ok();
        } else {
            return RespResult.error(ErrorConstant.COPY_FILE_ERR);
        }
    }

    @Override
    public RespResult putFileByIs(String resourceKey, InputStream is, String oringinFileName) {
        File destFile = new File(prePath, resourceKey);
        try {
            FileUtils.copyInputStreamToFile(is, destFile);
        } catch (IOException e) {
            return RespResult.error(e.getMessage());
        }
        return RespResult.ok();
    }

    @Override
    public RespResult putFile(String resourceKey, File file) {
        if (!file.exists()) {
            return RespResult.error(RespCodeEnum.FILE_NOT_EXISTED);
        }
        File destFile = new File(prePath, resourceKey);
        boolean result = FsUtil.copyFile(file, destFile);
        if (result) {
            return RespResult.ok();
        } else {
            return RespResult.error(ErrorConstant.PUT_FILE_ERR);
        }
    }

    @Override
    public RespResult deleteFile(String key) {
        File file = new File(prePath, key);
        if (!file.exists()) {
            return RespResult.error(RespCodeEnum.FILE_NOT_EXISTED);
        }
        FsUtil.delFile(file.getAbsolutePath());
        return RespResult.ok();
    }

    @Override
    public RespResult<File> getFile(String resourceKey, String toFilePath) {
        File srcFile = new File(prePath, resourceKey);
        if (!srcFile.exists()) {
            return RespResult.error(RespCodeEnum.FILE_NOT_EXISTED);
        }
        File destFile = new File(toFilePath);
        boolean result = FsUtil.copyFile(srcFile, destFile);
        if (result) {
            return RespResult.ok();
        } else {
            return RespResult.error(ErrorConstant.DOWNLOAD_FILE_ERR);
        }
    }

    @Override
    public RespResult moveFile(String key, String filePath) {
        return putFile(key, new File(filePath));
    }

    @Override
    public UrlValidResult validUrl(String url) {
        try {
            UrlParseResult urlParseResult = null;
            if (url.startsWith(hostLan)) {
                urlParseResult = this.parseUrl(hostLan, url);
            } else if (url.startsWith(hostWan)) {
                urlParseResult = this.parseUrl(hostWan, url);
            }
            if (null == urlParseResult || urlParseResult.getParams().size() != 3) {
                return new UrlValidResult(HttpStatus.UNAUTHORIZED, "Url Unauthorized", urlParseResult.getOssKey());
            }

            UrlParams urlParams = this.parseUrlParams(urlParseResult.getParams());

            String body = JSONUtil.toJson(new UrlSignBody(urlParseResult.getHost(), urlParams.getExpire(), urlParseResult.getOssKey()), UrlSignBody.class);

            String sign = MacUtil.sign(urlSecretKey, body, MacUtil.HMAC_SHA_256);
            if (!sign.equals(urlParams.getSign())) {
                return new UrlValidResult(HttpStatus.UNAUTHORIZED, "Url Unauthorized", urlParseResult.getOssKey());
            }
            // 验签成功，校验过期时间是否过期
            if (null != urlParams.getExpire()) {
                if (urlParams.getExpire() < System.currentTimeMillis()) {
                    return new UrlValidResult(HttpStatus.UNAUTHORIZED, "Url access expired", urlParseResult.getOssKey());
                }
            }
            // 校验ossKey是否存在
            File ossFile = new File(prePath, urlParseResult.getOssKey());
            if(!ossFile.exists()) {
                return new UrlValidResult(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.getReasonPhrase(), urlParseResult.getOssKey());
            }

            return new UrlValidResult(HttpStatus.OK, null, urlParseResult.getOssKey());
        } catch (Exception e) {
            return new UrlValidResult(HttpStatus.UNAUTHORIZED, "Url Unauthorized", null);
        }
    }

    private UrlParseResult parseUrl(String host, String url) {
        Map<String, String> params = new HashMap<>();
        String subUrl = url.substring(host.length());
        int index = subUrl.indexOf("?");
        if (index == -1) {
            return new UrlParseResult(host, subUrl, params);
        }

        String ossKey = subUrl.substring(0, index);

        String paramsStr = subUrl.substring(index + 1);

        if (StringUtils.isBlank(paramsStr)) {
            return new UrlParseResult(host, ossKey, params);
        }

        String[] paramItems = paramsStr.split("&");

        for (String paramItem : paramItems) {
            String[] split = paramItem.split("=");
            params.put(split[0], split[1]);
        }

        return new UrlParseResult(host, ossKey, params);
    }


    private UrlParams parseUrlParams(Map<String, String> paramsMap) {

        String version = paramsMap.get("version");
        String sign = paramsMap.get("sign");
        Long expire = null;

        String expireStr = paramsMap.get("expire");
        if (StringUtils.isNotBlank(expireStr)) {
            expire = Long.parseLong(expireStr);
        }

        return new UrlParams(version, sign, expire);

    }
}
